# -*- coding: utf-8 -*-
import commonlib
import traceback
import os, re
import sys
import html2dict
import time, datetime, workerpool
from MongoModel import MongoModel
from CrawlerLib import Log, Http
from lxml import etree


DOWNLOAD_PATH = '/home/hoangnamhai/HarvestedData/tintuc/tym/xalo/'
MONGO_SERVER = 'beta.mana.vn'    # Server Mana 64bit
MONGO_PORT = 27017
TYM_DB = 'my_database'
PREFIX = '/uploads/tym/'

mlogger = Log.getMongoLog(MONGO_SERVER, MONGO_PORT, 'm.xalo.vn')
logger = Log.getLogger('m.xalo.vn')
iphoneUserAgent = 'Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us) AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile/7A341 Safari/528.16'
header = {'User-Agent': 'Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us) AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile/7A341 Safari/528.16'}

class XaLo_Tym(MongoModel):
    __maxPage = 3      # số trang tối đa mỗi chuyên mục sẽ lấy
    chuyenMuc = {
        'xahoi': unicode('Xã hội', 'utf8'),
        'thethao': unicode('Thể thao', 'utf8'),
        'kinhdoanh': unicode('Kinh doanh', 'utf8'),
        'giaitri': unicode('Giải trí', 'utf8'),
        'giaoduc': unicode('Giáo dục', 'utf8'),
        'phapluat': unicode('Pháp luật', 'utf8'),
        'suckhoe': unicode('Sức khỏe', 'utf8'),
        'tinhyeugioitinh': unicode('Giới tính', 'utf8'),
        'khoahoc': unicode('Khoa học', 'utf8'),
        'congngheso': unicode('Công nghệ số', 'utf8'),
        'otoxemay': unicode('Ô tô - Xe máy', 'utf8'),
        'chuyenla': unicode('Chuyện lạ', 'utf8')
    }

    def __init__(self, host, port):
        MongoModel.__init__(self, host, port)
        self.authorId = self.findCrawlerAuthorId()

    def standardlizeTimeValue(self, timeStr):
        ''' Dùng để tính lại thời gian với các giá trị kiểu như: 34 phút trước, 1 giờ trước, hôm qua, hôm kia, 08/11/2010 '''
        timeStr = commonlib.encodeUTF8Str(timeStr)
        now = datetime.datetime.now()
        listPat = {'phut': re.compile(r"(\d+)\sphút trước"),
                   'gio': re.compile(r"(\d+)\sgiờ trước"),
                   'homqua': re.compile(r"hôm qua"),
                   'homkia': re.compile(r"hôm kia"),
                   'ngay': re.compile(r"(\d+)/(\d+)/(\d+)")
                   }
        result = now
        try:
            for patName in listPat.keys():
                m = listPat[patName].search(timeStr)
                if m is not None:
                    if patName == 'phut':
                        result = now - datetime.timedelta(minutes = int(m.group(1)))
                    elif patName == 'gio':
                        result = now - datetime.timedelta(hours = int(m.group(1)))
                    elif patName == 'homqua':
                        result = now - datetime.timedelta(days = 1)
                    elif patName == 'homkia':
                        result = now - datetime.timedelta(days = 2)
                    elif patName == 'ngay':
                        result = datetime.datetime(day = int(m.group(1)), month = int(m.group(2)), year = int(m.group(3)),
                                                   hour = now.hour, minute = now.minute, second = now.second)
            if (now - result).seconds > 864000 or (result > datetime.datetime.now()): raise
        except:
            return datetime.datetime.utcnow() - datetime.timedelta(seconds=1500)
        return result + datetime.timedelta(seconds=time.timezone)            
        
    def getUrlMainPart(self, url):
        ''' vd: input
            http://m.xalo.vn/17___5_xahoib_1_______.mtt_5_xahoi1_1__ffffcb632a528020_9cb3ed7____.mad
            http://m.xalo.vn/1_o_7_suckhoe1_1__12cbe463160_8932e5b____.mad '''
        result = commonlib.extractWithRegEx(r"__([0-9,a-f]+)_[0-9,a-f]+__", url, 1).strip()
        if result == '': result = url
        return result

    def getLeftRootOfCategory(self, catName):
        '''Dùng để lấy ra lft và root_id từ collection category để insert vào collection article tym'''
        db = self.connection[TYM_DB]
        category_types = db['categorytype']
        _type = category_types.find_one({'name': 'news'}, {'_id': 1})
        if _type==None: return None
        collection = db['category']
        q = collection.find_one({'data': catName, 'type_id': _type['_id']}, {'lft': 1, 'root_id': 1})
        result = {'category': q['lft'], 'root': q['root_id']} if q else ''
        return result

    def findCrawlerAuthorId(self, name='crawler'):
        '''Trả về object id của user crawler'''
        authorId = ''
        try:
            from pymongo.objectid import ObjectId
            db = self.connection[TYM_DB]
            collection = db['backend_user']
            cr = collection.find_one({'username': 'crawler'}, {'_id': 1})
            if cr is not None:
                authorId = ObjectId(cr['_id'])
        except:
            logger.exception(traceback.format_exc())
        finally:
            return authorId

    def save(self, args, chuyenMucId):
        try:
            db = self.connection[TYM_DB]
            collection = db['article']
            cat = self.getLeftRootOfCategory(self.chuyenMuc[chuyenMucId])
            if cat == '': return ''
            args['root'] = cat['root']
            args['category'] = cat['category']
            collection.save(args)
        except:
            logger.exception(str(sys.exc_info()[1]))

    def standardImgTag(self, base_url, node):
        logger.debug('call standardImgTag')
        '''Trong phan content, du lieu html crawled ve tat ca image deu dat trong the <a>Tai</a>
            - can replace the <a>Tai</a> thanh <img src=..>
        '''
        if node is None: return ''
        if commonlib.dataTypeName(node) == 'list':
            if len(node) == 0: return '' 
            node = node[0]
        for i in node.iterdescendants(tag='a'):
            if i.text != unicode('Tải', 'utf8'): continue
            p = i.getparent()
            if p == None: continue
            src = self.downloadImage(base_url, i.get('href'))
            if src == '': 
                p.remove(i)
            else:
                p.replace(i, etree.Element('img', {'src': src}))

    def downloadImage(self, base_url, str_inp):
        img = commonlib.unquoteUrl(str_inp)
        img = img.replace("type=ni", "type=JPEG")
        if img == '': return ''
        return commonlib.downloadNUpload(ssh, commonlib.urlJoin(base_url, img), DOWNLOAD_PATH, prefix=PREFIX)

    def toSMSContent(self, mobileContent):
        s = ''
        for line in mobileContent:
            if line['type'] != 'image': s += commonlib.toAscii(line['data']) + '\n'
        return s
        
    def toWebContent(self, mcontent):
        data = ""
        for item in mcontent:
            if item['type'] == 'image':
                data += "<img src='{0}' />\n".format(item['data'])
            elif item['type'] == 'textbold':
                data += "<b>{0}</b>\n".format(item['data'])
            else:
                data += "<p>{0}</p>\n".format(item['data'])
        return data

    def cleanContent(self, mcontent):
        data = []
        firstLine = mcontent[0]['data']
        if len(firstLine) < 5 and not re.search(r'[a-zA-Z]', firstLine): del mcontent[0]
            
        for item in mcontent:
            if item['type'] != 'image':
                if re.search(r'TAGS:.+', item['data']): break
                if re.search(r'Video: ', item['data']): continue
                if re.search(r'\(kỳ \d+\)', item['data']): continue
                if re.search(r'Tin liên quan', item['data']): continue
                if commonlib.wordCount(item['data']) < 5:
                    if re.search(r'Theo\s.+', item['data']): continue
            data.append(item)
        return data

    def processDetail(self, url, thumbnail=''):
        logger.debug('call processDetail({0})'.format(url))
        mData = []
        pages = 0
        nguonTin = ''
        try:
            while url != '':
                pages += 1
                tree = commonlib.getXMLTree(url, root_url='http://m.xalo.vn/tintuc.mobi', userAgent=iphoneUserAgent)
                if tree == '' or tree == None: break
                contentNode = tree.xpath("//div[@id='top']")
                commonlib.cleanElementWithAttrib(contentNode, 'td', [('style', 'BORDER-BOTTOM: #4a84b5 1 p</div><div class=')])
                # loại bỏ phần tiêu đề và nguồn tin khỏi chi tiết tin
                if pages == 1: 
                    nguonTin = commonlib.dropTagWithIndex(contentNode, 'span', 1)
                    nguonTin = commonlib.replaceStr(r"Nguồn:", '', nguonTin)
                    commonlib.dropTagWithIndex(contentNode, 'b', 1)
                if len(contentNode) == 0: break
                self.standardImgTag(url, contentNode)
                contentData = html2dict.html2text(etree.tounicode(contentNode[0]))
                contentData = self.cleanContent(contentData)
                for item in contentData:
                    if item['type'] == 'image':
                        if thumbnail == '': thumbnail = item['data']
                    if item.has_key('hash'): del item['hash']
                    mData.append(item)
                nextNode = tree.xpath("//a[contains(., 'Tiếp')]".decode('utf-8'))
                url = commonlib.urlJoin(url, commonlib.getAttribText(nextNode, 'href')) if len(nextNode) > 0 else ''
#            for i in mData:
#                print i['type'], '::', i['data']
        except:
            logger.exception(traceback.format_exc())
        wData = self.toWebContent(mData)
        smsData = self.toSMSContent(mData)
        return wData, mData, smsData, nguonTin

    def processPageList(self, url, chuyenMucId, currentProcessThreshold=0, currentDupThresHold=0):
        logger.debug('call processPageList({0}, {1})'.format(chuyenMucId, url))
        try:
            tree = commonlib.getXMLTree(url, root_url='http://m.xalo.vn/tintuc.mobi', userAgent=iphoneUserAgent)
            
            print etree.tostring(tree);
            
            if tree == '' or tree == None:
                return currentProcessThreshold, currentDupThresHold
            
            db = self.connection[TYM_DB]
            collection = db['article']
            # lay ra category va left_id tu collection category voi param la name of collection
            cat = self.getLeftRootOfCategory(self.chuyenMuc[chuyenMucId])
            if cat == '': return ''
            contentXpath = ''
            for artElem in tree.xpath("//a[contains(., 'Tổng hợp tin')]/../following-sibling::*[1]/div".decode('utf-8')):
                try:
                    if contentXpath == '': 
                        contentXpath = commonlib.getAttribText(artElem, 'class', '')
                    else:
                        if contentXpath != commonlib.getAttribText(artElem, 'class', ''): break
                    articleLink = commonlib.getAttribText(artElem.xpath('.//a'), 'href')
                    articleLink = commonlib.urlJoin(url, articleLink)
                    articleTitle = commonlib.getElementText(artElem.xpath('.//a'))
                    if re.search(r'Video:.+', articleTitle): continue
                    # 12/7/2010: Do xa lộ có link bài viết biến động theo ngày nên phải thêm hàm getUrlMainPart
                    # để xác định phần cố định của URL này rồi mới chuyển vào để làm ID
        #            result['_id'] = getMD5Hash(getUrlMainPart(result['link']))
                    hashLink = commonlib.getMD5Hash("{0}-{1}".format(self.getUrlMainPart(articleLink), articleTitle))
                    isExist = collection.find_one({'hashLink': hashLink})
                    if not isExist:
                        articleSource = commonlib.replaceStr(r'\s|\-', '', commonlib.getElementText(artElem.xpath('.//span[1]')))
                        print '--------------'
                        print commonlib.getElementText(artElem.xpath('.//span[2]'))
                        reportTime = self.standardlizeTimeValue(commonlib.getElementText(artElem.xpath('.//span[2]')))
                        print reportTime
                        print '--------------'
                        thumbnail = ''
                        thumbNode = artElem.xpath('.//img')
                        if len(thumbNode) > 0:
                            thumbLink = commonlib.urlJoin(url, commonlib.getAttribText(thumbNode, 'src'))
                            thumbnail = commonlib.downloadNUpload(ssh, thumbLink, DOWNLOAD_PATH, prefix=PREFIX)

                        webContent, mobileContent, smsContent, nguonTin = self.processDetail(articleLink, thumbnail)
                        if nguonTin == '': nguonTin = articleSource 
                        if len(mobileContent) > 0 and webContent != '':
                            args = {
                                'title': articleTitle,
                                'author_id': self.authorId,
                                'link': articleLink,
                                'hashLink': hashLink,
                                'source': nguonTin,
                                'thumbnail': thumbnail,
                                'content': {
                                    'mobile': mobileContent,
                                    'sms': smsContent,
                                    'web': webContent
                                },
                                'update': reportTime,
                                'created_at': datetime.datetime.utcnow(),
                                'date': datetime.datetime.utcnow(),
                                'type': 'text',
                                'is_active': True,
                                'timestamp': time.time(),
                                'lastupdate': datetime.datetime.utcnow(),
                            }
                            self.save(args, chuyenMucId)
                            currentProcessThreshold += 1
                            print('Title: %s' % articleTitle)
                            print('Link: %s' % articleLink)
                            print('----------WEB CONTENT-------------')
                            print(webContent)
                            print('----------MOBILE CONTENT-------------')
                            for i in mobileContent:
                                print(i['type'] + ': ' + i['data'])
                    else:
                        currentDupThresHold += 1
                        logger.info('Article existed: %s' % articleLink)
                except:
                    logger.exception(str(sys.exc_info()[1]))

            logger.info('{0}: Current procesed = {1}; Current dup = {2}'.format(chuyenMucId, currentProcessThreshold, currentDupThresHold))
        except:
            logger.exception(str(sys.exc_info()[1]))
        finally:
            return currentProcessThreshold, currentDupThresHold

    def processPageList2(self, url, chuyenMucId, currentProcessThreshold=0, currentDupThresHold=0):
        ''' Các anh Xa lộ củ chuối, từ trang 6 trở đi các anh ý format kiểu khác. Vậy nên sinh ra hàm này để
            xử lý các trang với p>=6.
            Chú ý:
                - trong page listing dạng này các bài viết không có thông tin về nguồn tin.
                - không có thông tin về ảnh thumbnail
        '''
        logger.debug('start processPageList2(%s, %s)' % (chuyenMucId, url))
        try:
            tree = commonlib.getXMLTree(url, root_url='http://m.xalo.vn/tintuc.mobi', userAgent=iphoneUserAgent)
            
            if tree == '' or tree == None:
                return currentProcessThreshold, currentDupThresHold
            db = self.connection[TYM_DB]
            collection = db['article']
            # lay ra category va left_id tu collection category voi param la name of collection
            cat = self.getLeftRootOfCategory(self.chuyenMuc[chuyenMucId])
            if cat == '': return ''
            contentXpath = ''
            
            for artElem in tree.xpath("//a[contains(., 'Tổng hợp tin')]/../following-sibling::*[1]/div".decode('utf-8')):
                try:
                    if contentXpath == '': 
                        contentXpath = commonlib.getAttribText(artElem, 'class', '')
                    else:
                        if contentXpath != commonlib.getAttribText(artElem, 'class', ''): break
                    articleLink = commonlib.getAttribText(artElem.xpath('.//a'), 'href')
                    articleLink = commonlib.urlJoin(url, articleLink)
                    articleTitle = commonlib.getElementText(artElem.xpath('.//a'))
                    # 12/7/2010: Do xa lộ có link bài viết biến động theo ngày nên phải thêm hàm getUrlMainPart
                    # để xác định phần cố định của URL này rồi mới chuyển vào để làm ID
        #            result['_id'] = getMD5Hash(getUrlMainPart(result['link']))
                    thumbnail = ''
                    hashLink = commonlib.getMD5Hash("{0}-{1}".format(self.getUrlMainPart(articleLink), articleTitle))
                    isExist = collection.find_one({'hashLink': hashLink})
                    if not isExist:
                        print '-----------'
                        print commonlib.getElementText(artElem.xpath('.//span[1]'))
                        reportTime = self.standardlizeTimeValue(commonlib.getElementText(artElem.xpath('.//span[1]')))
                        print reportTime
                        print '-----------'
                        webContent, mobileContent, smsContent, nguonTin = self.processDetail(articleLink, thumbnail)
                        if len(mobileContent) > 0 and webContent != '':
                            args = {
                                'title': articleTitle,
                                'author_id': self.authorId,
                                'link': articleLink,
                                'hashLink': hashLink,
                                'source': nguonTin,
                                'thumbnail': '',
                                'content': {
                                    'mobile': mobileContent,
                                    'sms': smsContent,
                                    'web': webContent
                                },
                                'update': reportTime,
                                'created_at': datetime.datetime.utcnow(),
                                'date': datetime.datetime.utcnow(),
                                'type': 'text',
                                'is_active': True,
                                'timestamp': time.time(),
                                'lastupdate': datetime.datetime.utcnow(),
                            }
                            self.save(args, chuyenMucId)
                            currentProcessThreshold += 1
                            print('Title: %s' % articleTitle)
                            print('Link: %s' % articleLink)
                            print('----------WEB CONTENT-------------')
                            print(webContent)
                            print('----------MOBILE CONTENT-------------')
                            for i in mobileContent:
                                print(i['type'] + ': ' + i['data'])
                    else:
                        currentDupThresHold += 1
                        logger.info('Article existed: %s' % articleLink)
                except:
                    logger.exception(str(sys.exc_info()[1]))
            logger.info('{0}: Current procesed = {1}; Current dup = {2}'.format(chuyenMucId, currentProcessThreshold, currentDupThresHold))
        except:
            logger.exception(str(sys.exc_info()[1]))
        finally:
            return currentProcessThreshold, currentDupThresHold

    def processChuyenMuc(self, chuyenMucId):
        duplicateThreshold = 10
        maxProcessThreshold = 30
        currentDupThresHold = 0
        currentProcessThreshold = 0
        global totalNewsCrawlered, totalNewsDuplicated
        cat = self.getLeftRootOfCategory(self.chuyenMuc[chuyenMucId])
        if cat == '':
            logger.info('chuyen muc: "%s" khong ton tai' % self.chuyenMuc[chuyenMucId])
            return ''
        try:
            for pageIndex in range(1, self.__maxPage + 1):
                if currentDupThresHold >= duplicateThreshold:
                    logger.info('Chuyên mục: {0} dừng vì trùng quá ngưỡng cho phép'.format(chuyenMucId))
                    return ''
                if currentProcessThreshold >= maxProcessThreshold:
                    logger.info('Chuyên mục: {0} dừng vì đã xử lý nhiều quá ngưỡng cho phép'.format(chuyenMucId))
                    return ''
                url = 'http://m.xalo.vn/{0}.mcm?p={1}&t={2}'.format(chuyenMucId, pageIndex, time.time())
                if pageIndex <= 5:
#                    currentProcessThreshold, currentDupThresHold = self.processPageList(url, chuyenMucId, currentProcessThreshold, currentDupThresHold)
                    currentProcessThreshold, currentDupThresHold = self.getNewsLink(url, chuyenMucId, currentProcessThreshold, currentDupThresHold)
#                else:
#                    currentProcessThreshold, currentDupThresHold = self.processPageList2(url, chuyenMucId, currentProcessThreshold, currentDupThresHold)
            totalNewsCrawlered += currentDupThresHold
            totalNewsDuplicated += currentDupThresHold
        except:
            logger.error(str(sys.exc_info()[1]))
        return ''

    def getNewsLink(self, url, chuyenMucId, currentProcessThreshold=0, currentDupThresHold=0):
        try:
            tree = Http.getXMLTree(url)
            if tree == None: raise Exception, 'Khong lay duoc link bai viet cua chuyen muc {0}'.format(chuyenMucId)
            for node in tree.xpath("//a[contains(@href, '__.mad')]"):
                link = commonlib.getAttribText(node, 'href')
                if link != '':
                    link = commonlib.urlJoin(url, link)
                    print link
        except:
            logger.exception(str(sys.exc_info()[1]))
        return currentProcessThreshold, currentDupThresHold

def forceQuit():
    os._exit(1)

def quitIfTimeout():
    logger.debug('call quitIfTimeout')
    while True:
        delta = time.time() - lastaction
        pid = os.getpid()
        try:
            if delta > 600:
                if totalNewsCrawlered == 0 and totalNewsDuplicated == 0:
                    mlogger.critical(unicode("crawler tin tức m.xalo.vn không hoạt động", 'utf8'))
                    logger.critical(unicode("crawler tin tức m.xalo.vn không hoạt động", 'utf8'))
                else:
                    logger.info(unicode("Tổng số bài viết crawled: {0}, tổng số bài trùng lặp {1}".format(totalNewsCrawlered, totalNewsDuplicated), 'utf8'))
                logger.info('process timeout {0}'.format(delta))
                logger.info('kill process {0}'.format(pid))
                os.system("kill -9 {0}".format(pid))
        except:
            logger.error('ERROR: could not kill python process with pid={0}'.format(pid))
        time.sleep(10)

if __name__ == '__main__':
    totalNewsCrawlered = 0
    totalNewsDuplicated = 0
    import argparse
    import threading
    lastaction = time.time()
    # -- Phần argument command line
    parser = argparse.ArgumentParser(description="Crawler tin tức")
    parser.add_argument('--run', action='store', dest='run_mode',
                    help='sftp: chạy dưới local và upload file lên server thông qua sftp', metavar='sftp')
    args = parser.parse_args()
    # -----------------------------
    # -- Phần khởi tạo kết nối ssh tới server mana.vn
    run_mode = args.run_mode
    ssh = None
    if run_mode == 'sftp':
        ssh = commonlib.getSSH('mana.vn', 'giangnh')
        if ssh == None:
            print 'Không kết nối được tới server, thoát crawler'
            forceQuit()
    # -----------------------------
    threading.Thread(target=quitIfTimeout).start()
    logger.info('start crawler xalo tin tuc for tym')
    xlt = XaLo_Tym(MONGO_SERVER, MONGO_PORT)
#    xlt.processDetail('http://m.xalo.vn/1_o_9_kinhdoanh1_1__13540d511c0_31f003f____.mad')
    if xlt.authorId == '':
        logger.warning("user crawler doesn't exist. stop crawler now. You should to create an username: crawler")
        os._exit(1)
    try:
        pool = workerpool.WorkerPool(size=1)
        pool.map(xlt.processChuyenMuc, xlt.chuyenMuc.keys())
        pool.shutdown()
        pool.wait()
    except:
        logger.error(str(sys.exc_info()[1]))
    if ssh is not None: ssh.close()
    if totalNewsCrawlered == 0 and totalNewsDuplicated == 0:
        logger.critical(unicode("crawler tin tức m.xalo.vn không hoạt động", 'utf8'))
        mlogger.critical(unicode("crawler tin tức m.xalo.vn không hoạt động", 'utf8'))
    else:
        logger.info(unicode("Tổng số bài viết crawled: {0}, tổng số bài trùng lặp {1}".format(totalNewsCrawlered, totalNewsDuplicated), 'utf8'))
    logger.info('finished crawler xalo tin tuc for tym at %s' % str(datetime.datetime.now()))
    os._exit(1)
